package biolabs.space;


/**
 * Abstract class for a position in a vector space.
 *
 * Methods {@link #equals(Object)} and {@link #hashCode()}
 * throw an {@link AbstractMethodError} so they must be
 * overridden to provide a meaningful implementation.
 *
 * @param <T> this type
 * @param <S> the type of scalar values (the field over which the vector space is defined)
 *
 * @author Tomas Mikula
 */
public abstract class Position<T extends Position<T, S>, S extends Number> {

	/**
	 * Returns the number of coordinates of this position,
	 * that is the dimension of the corresponding vector space.
	 */
	public abstract int getDimension();

	/**
	 * Returns the {@code i}-th coordinate of this position.
	 * @throws IndexOutOfBoundsException if {@code i} is negative or greater
	 * than {@code d-1}, where {@code d} is the number of coordinates
	 * of this position, as returned by {@link #getDimension()}.
	 */
	public abstract S getCoordinate(int i);

	/**
	 * Binary vector addition.
	 * @return A new position which is the sum of
	 *   {@code this} and {@code a} ({@code this} + {@code a}).
	 */
	public abstract T plus(T a);

	/**
	 * Binary vector subtraction.
	 * @return A new position which is the difference of
	 *   {@code this} and {@code a} ({@code this} - {@code a}).
	 */
	public abstract T minus(T a);

	/**
	 * Multiplication by a scalar.
	 * @param k scalar factor
	 * @return A new position equal to {@code k} times {@code this}.
	 */
	public abstract T times(S k);

	/**
	 * Returns the inner product of {@code this} and {@code a}.
	 */
	public abstract S innerProduct(T a);

	/**
	 * Returns the distance of this position
	 * from the origin of the vector space.
	 */
	public abstract double length();

	/**
	 * Returns a zero vector.
	 */
	public abstract T zero();

	/**
	 * Returns a vector of ones.
	 */
	public abstract T one();

	/**
	 * Returns a new position whose {@code idx}-th coordinate will be
	 * equal to {@code value} and the remaining ones will be unchanged.
	 * @param idx zero-based index of the coordinate to change
	 * @param value the new value of the appointed coordinate
	 * @throws IndexOutOfBoundsException if {@code idx} is negative or
	 *   greater than {@code getDimension()-1}
	 */
	public abstract T withChangedCoordinate(int idx, S value)
			throws IndexOutOfBoundsException;

	/**
	 * Compares two positions for equality.
	 * This method should be preferred to {@link #equals(Object)},
	 * because it enforces the correct parameter type, thus cannot
	 * be accidentally called with a wrong argument.
	 */
	public abstract boolean isEqualTo(T p);

	/**
	 * This implementation throws AbstractMethodError to indicate
	 * that it should be overridden in the subclass.
	 * @throws AbstractMethodError
	 * @deprecated in favor of {@link #isEqualTo(Position)} which
	 *   avoids accidentally passing object of wrong type
	 */
	@Override
	@Deprecated
	public boolean equals(Object o){
		throw new AbstractMethodError("equals() should be overridden in a subclass");
	}

	/**
	 * This implementation throws AbstractMethodError to indicate
	 * that it should be overridden in the subclass.
	 * @throws AbstractMethodError
	 */
	@Override
	public int hashCode(){
		throw new AbstractMethodError();
	}

	/**
	 * This implementation throws AbstractMethodError to indicate
	 * that it should be overridden in the subclass.
	 * @throws AbstractMethodError
	 */
	@Override
	public String toString(){
		throw new AbstractMethodError();
	}
}
